package com.adee.java.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.*;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
import java.util.Set;

public class HtmlNioServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        SelectionKey selectionKey = ssc.register(selector, SelectionKey.OP_ACCEPT, null);
        ssc.bind(new InetSocketAddress(8080));
        while (true) {
            int count = selector.select(); // 有空轮询bug，根据返回值count判断，返回值为0只有在超时时发生，否则就发生了空轮询
            if(0 == count) continue;
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey k = iterator.next();
                iterator.remove();
                if(k.isAcceptable()) {
                    ServerSocketChannel ch = (ServerSocketChannel)(k.channel());
                    SocketChannel sc = ch.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                } else if(k.isReadable()) {
                    SocketChannel sc = (SocketChannel) (k.channel());
                    byte[] bytes = new byte[64];
                    ByteBuffer buf = ByteBuffer.wrap(bytes);
                    int read = sc.read(buf);
                    if(read < 0) { // 断开
                        sc.close();
                        continue;
                    }
                    System.out.println(new String(bytes, 0, read));
                    // sendHtmlBySendfile(sc); // sendfile方式
                    sendHtmlByMmap(sc); // mmap方式
                }
            }
        }
    }

    // 使用sendfile方式传输文件内容到socket，sendfile方式适合大文件传输，因为不用设置缓冲区大小，底层为流式传输
    private static void sendHtmlBySendfile(SocketChannel sc) throws IOException {
        String from = "F:\\mycode\\java\\jdk\\src\\main\\resources/txt/sendfile.from.txt";
        FileChannel readChannel = FileChannel.open(Paths.get(from), StandardOpenOption.READ);
        readChannel.transferTo(0, readChannel.size(), sc);
        readChannel.close();
    }

    // 使用mmap方式传输文件内容到socket，适合小数据量的两个fd之间传输，因为需要给mmap设置缓冲区大小，不宜设置过大
    private static void sendHtmlByMmap(SocketChannel sc) throws IOException {
        String from = "F:\\mycode\\java\\jdk\\src\\main\\resources/txt/mmap.from.txt";
        FileChannel readChannel = FileChannel.open(Paths.get(from), StandardOpenOption.READ);
        MappedByteBuffer mappedByteBuffer = readChannel.map(
                FileChannel.MapMode.READ_ONLY, 0, readChannel.size());
        sc.write(mappedByteBuffer);
        readChannel.close();
    }
}
